package com.lightboat.common.algorithm;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;

/**
 * 时间换空间的实时计算抽取算法
 * 该类实现了基于权重的实时抽取逻辑，适用于抽取数据数量较少的场景。
 */
public class RealTimeLottery<T> {

    // 抽取数据列表，包含每个抽取数据的权重信息
    private List<WeightedLotteryWheel.Prize<T>> prizes;

    /**
     * 构造函数，初始化抽取数据列表
     *
     * @param prizes 抽取数据列表
     */
    public RealTimeLottery(List<WeightedLotteryWheel.Prize<T>> prizes) {
        this.prizes = new ArrayList<>(prizes);
    }

    /**
     * 实时计算抽取结果
     * 时间复杂度：O(n)，需要遍历抽取数据列表计算累积权重
     * 空间复杂度：O(1)，无需额外存储结构
     *
     * @return 抽中的抽取数据信息
     */
    public WeightedLotteryWheel.Prize<T> drawPrize() {
        if (prizes.isEmpty()) {
            return null; // 如果抽取数据列表为空，直接返回null
        }

        // 计算总权重
        int totalWeight = prizes.stream()
                .mapToInt(WeightedLotteryWheel.Prize<T>::getWeight)
                .sum();

        if (totalWeight == 0) {
            return null; // 如果总权重为0，无法进行抽取
        }

        // 生成随机数
        int randomValue = ThreadLocalRandom.current().nextInt(1, totalWeight + 1);

        // 逐个比对权重区间，找到对应的抽取数据
        int currentSum = 0;
        for (WeightedLotteryWheel.Prize<T> prize : prizes) {
            currentSum += prize.getWeight();
            if (randomValue <= currentSum) {
                return prize;
            }
        }

        // 理论上不会到达这里，返回最后一个抽取数据作为默认值
        return prizes.get(prizes.size() - 1);
    }

    /**
     * 批量抽取的优化版本
     * 适用于需要一次性抽取多个抽取数据的场景
     *
     * @param count 抽取次数
     * @return 抽中的抽取数据列表
     */
    public List<WeightedLotteryWheel.Prize<T>> drawMultiplePrizes(int count) {
        if (count <= 0 || prizes.isEmpty()) {
            return Collections.emptyList(); // 如果抽取次数小于等于0或抽取数据列表为空，返回空列表
        }

        // 只计算一次总权重，避免重复计算
        int totalWeight = prizes.stream()
                .mapToInt(WeightedLotteryWheel.Prize<T>::getWeight)
                .sum();

        if (totalWeight == 0) {
            return Collections.emptyList(); // 如果总权重为0，无法进行抽取
        }

        List<WeightedLotteryWheel.Prize<T>> results = new ArrayList<>();
        ThreadLocalRandom random = ThreadLocalRandom.current();

        // 批量抽取逻辑
        for (int i = 0; i < count; i++) {
            int randomValue = random.nextInt(1, totalWeight + 1);

            int currentSum = 0;
            for (WeightedLotteryWheel.Prize<T> prize : prizes) {
                currentSum += prize.getWeight();
                if (randomValue <= currentSum) {
                    results.add(prize);
                    break;
                }
            }
        }

        return results;
    }
}